/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.github.nukc.stateview.view;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Arc;
import ohos.agp.render.Paint;
import ohos.agp.render.Paint.ShaderType;
import ohos.agp.render.Paint.Style;
import ohos.agp.render.Shader;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;

/**
 * ProgressWheel
 *
 * @since 2021-04-26
 */
public class ProgressWheel extends Component {
    private static final float RIM_WIDTH = 0.2f;
    private static final float CONTOUR_SIZE = 2.0f;
    private static final float BAR_WIDTH = 1.5f;
    private static final int INTIGER = 2;
    private static final int FULLRADIUS = 100;
    private static final int CIRCLERADIUS = 80;
    private static final int BARLENGTH = 60;
    private static final int BARWIDTH = 20;
    private static final int PADDING = 5;
    private static final int ANGLE = 360;
    private static final int ANGLE_90 = 90;
    private static final int ANGLE_NEGATIVE = -90;
    private static final int BARCOLOR = 0xAA000000;
    private static final int RIMCOLOR = 0xAADDDDDD;
    private static final int TEXTCOLOR = 0xFF000000;

    private int fullRadius = FULLRADIUS;
    private int circleRadius = CIRCLERADIUS;
    private int barLength = BARLENGTH;
    private int barWidth = BARWIDTH;
    private int rimWidth = BARWIDTH;
    private int textSize = BARWIDTH;
    private int layoutHeight = 0;
    private int layoutWidth = 0;

    private float contourSize = 0;

    private int paddingTop = PADDING;
    private int paddingBottom = PADDING;
    private int paddingLeft = PADDING;
    private int paddingRight = PADDING;

    private int barColor = BARCOLOR;
    private int contourColor = BARCOLOR;
    private int circleColor = 0x00000000;
    private int rimColor = RIMCOLOR;
    private int textColor = TEXTCOLOR;

    private Paint barPaint = new Paint();
    private Paint circlePaint = new Paint();
    private Paint rimPaint = new Paint();
    private Paint textPaint = new Paint();
    private Paint contourPaint = new Paint();

    private RectFloat innerCircleBounds = new RectFloat();
    private RectFloat circleBounds = new RectFloat();

    private float spinSpeed = CONTOUR_SIZE;
    private int delayMillis = FULLRADIUS;
    private float progress = 0;
    private boolean isSpinning = false;

    private String text = "";
    private String[] splitText = {};

    private EventHandler spinHandler = new EventHandler(EventRunner.getMainEventRunner()) {
        /**
         * This is the code that will increment the progress variable and so
         * spin the wheel
         *
         * @param msg msg
         */
        @Override
        public void processEvent(InnerEvent msg) {
            invalidate();
            if (isSpinning) {
                spinHandler.sendEvent(0, delayMillis);
            }
            super.processEvent(msg);
        }
    };

    /**
     * The constructor for the ProgressWheel
     *
     * @param context context
     * @param attrSet attrSet
     */
    public ProgressWheel(Context context, AttrSet attrSet) {
        super(context, attrSet);
        DrawTask task = (component, canvas) -> {
            setupBounds();
            setupPaints();
            canvas.drawArc(innerCircleBounds, new Arc(ANGLE, ANGLE, false), circlePaint);
            canvas.drawArc(circleBounds, new Arc(ANGLE, ANGLE, false), rimPaint);
            if (isSpinning) {
                canvas.drawArc(circleBounds, new Arc(progress - ANGLE_90, barLength, false), barPaint);
            } else {
                canvas.drawArc(circleBounds, new Arc(ANGLE_NEGATIVE, progress, false), barPaint);
            }
            float textHeight = textPaint.descent() - textPaint.ascent();
            float verticalTextOffset = (textHeight / INTIGER) - textPaint.descent();
            for (String line : splitText) {
                float horizontalTextOffset = textPaint.measureText(line) / INTIGER;
                canvas.drawText(
                        textPaint,
                        line,
                        (float) component.getWidth() / INTIGER - horizontalTextOffset,
                        (float) component.getHeight() / INTIGER + verticalTextOffset);
            }
            if (isSpinning) {
                scheduleRedraw();
            }
        };
        addDrawTask(task);
    }

    /**
     * Set the properties of the paints we're using to
     * draw the progress wheel
     */
    private void setupPaints() {
        barPaint.setColor(new Color(barColor));
        barPaint.setAntiAlias(true);
        barPaint.setStyle(Style.STROKE_STYLE);
        barPaint.setStrokeWidth(barWidth);

        rimPaint.setColor(new Color(rimColor));
        rimPaint.setAntiAlias(true);
        rimPaint.setStyle(Style.STROKE_STYLE);
        rimPaint.setStrokeWidth(rimWidth);

        circlePaint.setColor(new Color(circleColor));
        circlePaint.setAntiAlias(true);
        circlePaint.setStyle(Style.FILL_STYLE);

        textPaint.setColor(new Color(textColor));
        textPaint.setStyle(Style.FILL_STYLE);
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(textSize);

        contourPaint.setColor(new Color(contourColor));
        contourPaint.setAntiAlias(true);
        contourPaint.setStyle(Style.STROKE_STYLE);
        contourPaint.setStrokeWidth(contourSize);
    }

    /**
     * Set the bounds of the component
     */
    private void setupBounds() {
        // Width should equal to Height, find the min value to setup the circle
        int minValue = Math.min(layoutWidth, layoutHeight);

        // Calc the Offset if needed
        int offsetx = layoutWidth - minValue;
        int offsety = layoutHeight - minValue;

        // Add the offset
        paddingTop = this.getPaddingTop() + (offsety / INTIGER);
        paddingBottom = this.getPaddingBottom() + (offsety / INTIGER);
        paddingLeft = this.getPaddingLeft() + (offsetx / INTIGER);
        paddingRight = this.getPaddingRight() + (offsetx / INTIGER);

        int width = getWidth();
        int height = getHeight();

        innerCircleBounds = new RectFloat(
                paddingLeft + (BAR_WIDTH * barWidth),
                paddingTop + (BAR_WIDTH * barWidth),
                width - paddingRight - (BAR_WIDTH * barWidth),
                height - paddingBottom - (BAR_WIDTH * barWidth));
        circleBounds = new RectFloat(
                paddingLeft + barWidth,
                paddingTop + barWidth,
                width - paddingRight - barWidth,
                height - paddingBottom - barWidth);

        fullRadius = (width - paddingRight - barWidth) / INTIGER;
        circleRadius = (fullRadius - barWidth) + 1;
    }

    @Override
    public void addDrawTask(DrawTask drawTask) {
        super.addDrawTask(drawTask);
        drawTask.onDraw(this,mCanvasForTaskOverContent);
    }

    private void scheduleRedraw() {
        progress += spinSpeed;
        if (progress > ANGLE) {
            progress = 0;
        }
        invalidate();
    }

    public boolean isSpinning() {
        return isSpinning;
    }

    /**
     * Reset the count (in increment mode)
     */
    public void resetCount() {
        progress = 0;
        setText("0%");
        invalidate();
    }

    /**
     * Turn off startSpinning mode
     */
    public void stopSpinning() {
        isSpinning = false;
        progress = 0;
        invalidate();
    }

    /**
     * Puts the view on spin mode
     */
    public void startSpinning() {
        isSpinning = true;
        spinHandler.sendEvent(0);
    }

    /**
     * Increment the progress by 1 (of 360)
     */
    public void incrementProgress() {
        incrementProgress(1);
    }

    /** 递增进度
     *
     * @param amount 数量
     */
    public void incrementProgress(int amount) {
        isSpinning = false;
        progress += amount;
        if (progress > ANGLE) {
            progress %= ANGLE;
        }
        invalidate();
    }

    /** 设定进度
     *
     * @param ii 属性集合
     */
    public void setProgress(int ii) {
        isSpinning = false;
        progress = ii;
        invalidate();
    }

    /** 设定文字
     *
     * @param text 文字内容
     */
    public void setText(String text) {
        this.text = text;
        splitText = this.text.split(System.lineSeparator());
    }

    public int getCircleRadius() {
        return circleRadius;
    }

    public void setCircleRadius(int circleRadius) {
        this.circleRadius = circleRadius;
    }

    public int getBarLength() {
        return barLength;
    }

    public void setBarLength(int barLength) {
        this.barLength = barLength;
    }

    public int getBarWidth() {
        return barWidth;
    }

    /** 设置条宽
     *
     * @param barWidth 属性集合
     */
    public void setBarWidth(int barWidth) {
        this.barWidth = barWidth;

        if (this.barPaint != null) {
            this.barPaint.setStrokeWidth(this.barWidth);
        }
    }

    public int getTextSize() {
        return textSize;
    }

    /** 设置文字大小
     *
     * @param textSize 文字大小
     */
    public void setTextSize(int textSize) {
        this.textSize = textSize;

        if (this.textPaint != null) {
            this.textPaint.setTextSize(this.textSize);
        }
    }

    public int getPaddingTop() {
        return paddingTop;
    }

    public void setPaddingTop(int paddingTop) {
        this.paddingTop = paddingTop;
    }

    public int getPaddingBottom() {
        return paddingBottom;
    }

    public void setPaddingBottom(int paddingBottom) {
        this.paddingBottom = paddingBottom;
    }

    public int getPaddingLeft() {
        return paddingLeft;
    }

    public void setPaddingLeft(int paddingLeft) {
        this.paddingLeft = paddingLeft;
    }

    public int getPaddingRight() {
        return paddingRight;
    }

    public void setPaddingRight(int paddingRight) {
        this.paddingRight = paddingRight;
    }

    public int getBarColor() {
        return barColor;
    }

    /** 设置工具栏颜色
     *
     * @param barColor 工具栏颜色
     */
    public void setBarColor(int barColor) {
        this.barColor = barColor;

        if (this.barPaint != null) {
            this.barPaint.setColor(new Color(this.barColor));
        }
    }

    public int getCircleColor() {
        return circleColor;
    }

    /** 设置圆圈颜色
     *
     * @param circleColor 圆圈颜色
     */
    public void setCircleColor(int circleColor) {
        this.circleColor = circleColor;

        if (this.circlePaint != null) {
            this.circlePaint.setColor(new Color(this.circleColor));
        }
    }

    public int getRimColor() {
        return rimColor;
    }

    /** 设置边框颜色
     *
     * @param rimColor 边框颜色
     */
    public void setRimColor(int rimColor) {
        this.rimColor = rimColor;

        if (this.rimPaint != null) {
            this.rimPaint.setColor(new Color(this.rimColor));
        }
    }

    public Shader getRimShader() {
        return rimPaint.getShader();
    }

    /** 设置环明暗器
     *
     * @param shader 着色器
     * @param shaderType 着色器类型
     */
    public void setRimShader(Shader shader,ShaderType shaderType) {
        this.rimPaint.setShader(shader,shaderType);
    }

    public int getTextColor() {
        return textColor;
    }

    /** 设置文字颜色
     *
     * @param textColor 文字颜色
     */
    public void setTextColor(int textColor) {
        this.textColor = textColor;

        if (this.textPaint != null) {
            this.textPaint.setColor(new Color(this.textColor));
        }
    }

    public float getSpinSpeed() {
        return spinSpeed;
    }

    public void setSpinSpeed(float spinSpeed) {
        this.spinSpeed = spinSpeed;
    }

    public int getRimWidth() {
        return rimWidth;
    }

    /** 设置环宽度
     *
     * @param rimWidth 宽度
     */
    public void setRimWidth(int rimWidth) {
        this.rimWidth = rimWidth;

        if (this.rimPaint != null) {
            this.rimPaint.setStrokeWidth(this.rimWidth);
        }
    }

    public int getDelayMillis() {
        return delayMillis;
    }

    public void setDelayMillis(int delayMillis) {
        this.delayMillis = delayMillis;
    }

    public int getContourColor() {
        return contourColor;
    }

    /** 设置轮廓颜色
     *
     * @param contourColor 颜色
     */
    public void setContourColor(int contourColor) {
        this.contourColor = contourColor;

        if (contourPaint != null) {
            this.contourPaint.setColor(new Color(this.contourColor));
        }
    }

    public float getContourSize() {
        return this.contourSize;
    }

    /** 设置轮廓尺寸
     *
     * @param contourSize 尺寸
     */
    public void setContourSize(float contourSize) {
        this.contourSize = contourSize;
        if (contourPaint != null) {
            this.contourPaint.setStrokeWidth(this.contourSize);
        }
    }

    public int getProgress() {
        return (int) progress;
    }
}